﻿using Doolin.MongoRepository.Commons;
using MongoDB.Driver;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;

namespace Doolin.MongoRepository
{
  public abstract class BaseRepository<TEntity> : IRepository<TEntity> where TEntity : class,IMongoEntity
  {
    protected readonly IMongoDbContext _context;
    protected readonly IMongoCollection<TEntity> DbSet;

    protected BaseRepository(IMongoDbContext context)
    {
      _context = context;
      DbSet = _context.GetCollection<TEntity>(typeof(TEntity).Name);
    }

    public virtual Task Add(TEntity obj)
    {
      return _context.AddCommand(async () => await DbSet.InsertOneAsync(obj));
    }

    public virtual async Task<TEntity> GetById(string id)
    {
      var data = await DbSet.FindAsync(Builders<TEntity>.Filter.Eq("_id", id));
      return data.FirstOrDefault();
    }

    public virtual async Task<IEnumerable<TEntity>> GetAll()
    {
      var all = await DbSet.FindAsync(Builders<TEntity>.Filter.Empty);
      return all.ToList();
    }

    public virtual Task Update(TEntity obj)
    {
      
      return _context.AddCommand(async () =>
      {
        await DbSet.ReplaceOneAsync(Builders<TEntity>.Filter.Eq("_id", obj.Id), obj);
      });
    }

    public virtual Task Remove(string id) => _context.AddCommand(() => DbSet.DeleteOneAsync(Builders<TEntity>.Filter.Eq("_id", id)));

    public virtual IList<TEntity> GetFilteredEntities(Expression<Func<TEntity, bool>> filterExpression)
    {
      if (filterExpression != null)
        return DbSet.FindAsync(filterExpression).Result.ToList();

      else
        return GetAll().Result.ToList();
    }

    public void Dispose()
    {
      GC.SuppressFinalize(this);
    }

    public virtual async Task<Tuple<IList<TEntity>, long>> PagingAsync(Expression<Func<TEntity, bool>> expression, int page, int size)
    {
      var query = DbSet.Find(expression);
      var totalTask = query.CountDocumentsAsync();
      var itemsTask = query.Skip((page - 1) * size).Limit(size).ToListAsync();
      await Task.WhenAll(totalTask, itemsTask);
      return new Tuple<IList<TEntity>, long>(itemsTask.Result, totalTask.Result);
    }
  }
}
